#include "../lib_include/SocketsOps.h"
#include "../lib_include/Logger.h"
#include "../lib_include/Types.h"
#include "../lib_include/Endian.h"

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>  // snprintf
#include <sys/socket.h>
#include <sys/uio.h>  // readv
#include <unistd.h>

using namespace netlib;
using namespace netlib::net;

namespace
{

typedef struct sockaddr SA;


#if VALGRIND || defined (NO_ACCEPT4)
void setNonBlockAndCloseOnExec(int sockfd)
{
    // non-block
    int flags = ::fcntl(sockfd, F_GETFL, 0);
    flags |= O_NONBLOCK;
    int ret = ::fcntl(sockfd, F_SETFL, flags);
    // FIXME check

    // close-on-exec
    flags = ::fcntl(sockfd, F_GETFD, 0);
    flags |= FD_CLOEXEC;
    ret = ::fcntl(sockfd, F_SETFD, flags);
    // FIXME check

    (void)ret;
}
#endif

} // namespace end

// IPV6地址结构体指针转成通用的const socket结构体指针
const struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in6* addr)
{ return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr)); }

// IPV6地址结构体指针转成通用的socket结构体指针
struct sockaddr* sockets::sockaddr_cast(struct sockaddr_in6* addr)
{ return static_cast<struct sockaddr*>(implicit_cast<void*>(addr)); }

// IPV4地址结构体指针转成通用的const socket结构体指针
const struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in* addr)
{ return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr)); }

// 通用的socket结构体指针转成const IPv4结构体指针
const struct sockaddr_in* sockets::sockaddr_in_cast(const struct sockaddr* addr)
{ return static_cast<const struct sockaddr_in*>(implicit_cast<const void*>(addr)); }

// 通用的socket结构体指针转成const IPv6结构体指针
const struct sockaddr_in6* sockets::sockaddr_in6_cast(const struct sockaddr* addr)
{ return static_cast<const struct sockaddr_in6*>(implicit_cast<const void*>(addr)); }

// 创建非阻塞套接字
int sockets::createNonblockingOrDie(sa_family_t family)
{
    #if VALGRIND // VALGRIND用于检测内存泄露以及检查描述符的状态，用于DEBUG
    int sockfd = ::socket(family, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0)
        LOG_SYSFATAL << "sockets::createNonblockingOrDie";

    setNonBlockAndCloseOnExec(sockfd);
    #else // NDEBUG，也是创建非阻塞套接字
    int sockfd = ::socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
    if (sockfd < 0)
        LOG_SYSFATAL << "sockets::createNonblockingOrDie";
    #endif
    return sockfd;
}

// IP、端口号与套接字绑定
void sockets::bindOrDie(int sockfd, const struct sockaddr* addr)
{
    int ret = ::bind(sockfd, addr, static_cast<socklen_t>(sizeof(struct sockaddr_in6)));
    if (ret < 0)
        LOG_SYSFATAL << "sockets::bindOrDie";
}

// 开启监听套接字
void sockets::listenOrDie(int sockfd)
{
    int ret = ::listen(sockfd, SOMAXCONN);
    if (ret < 0)
        LOG_SYSFATAL << "sockets::listenOrDie";
}

// 从监听套接字接受连接
int sockets::accept(int sockfd, struct sockaddr_in6* addr)
{
    socklen_t addrlen = static_cast<socklen_t>(sizeof *addr);

    // VALGRIND用于检测内存泄露以及检查描述符的状态，用于DEBUG
    #if VALGRIND || defined (NO_ACCEPT4)
    int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);
    setNonBlockAndCloseOnExec(connfd);
    #else // NDEBUG，也是创建非阻塞套接字
    int connfd = ::accept4(sockfd, sockaddr_cast(addr),
                            &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
    #endif
    if (connfd < 0)
    {
        int savedErrno = errno;
        LOG_SYSERR << "Socket::accept";
        switch (savedErrno)
        {
            case EAGAIN:
            case ECONNABORTED:
            case EINTR:
            case EPROTO:
            case EPERM:
            case EMFILE: // 以上为非致命错误
            {
                errno = savedErrno;
                break;
            }
            case EBADF:
            case EFAULT:
            case EINVAL:
            case ENFILE:
            case ENOBUFS:
            case ENOMEM:
            case ENOTSOCK:
            case EOPNOTSUPP: // 这一栏为致命错误，退出程序
            {
                LOG_FATAL << "unexpected error of ::accept " << savedErrno;
                break;
            }
            default: // 未知错误，退出程序
            {
                LOG_FATAL << "unknown error of ::accept " << savedErrno;
                break;
            }
        }
    }
    return connfd;
}

// 主动发起连接
int sockets::connect(int sockfd, const struct sockaddr* addr)
{ return ::connect(sockfd, addr, static_cast<socklen_t>(sizeof(struct sockaddr_in6))); }

// 从描述符fd中读取数据
ssize_t sockets::read(int sockfd, void *buf, size_t count)
{ return ::read(sockfd, buf, count); }

// 从描述符中读取数据到分散的内存块
ssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt)
{ return ::readv(sockfd, iov, iovcnt); }

// 向套接字中写入数据
ssize_t sockets::write(int sockfd, const void *buf, size_t count)
{ return ::write(sockfd, buf, count); }

// 关闭套接字
void sockets::close(int sockfd)
{
    if (::close(sockfd) < 0)
        LOG_SYSERR << "sockets::close";
}

// 调用close()，不会直接关闭连接套接字，只是计数器减一，程序将等待缓冲区中排队的数据处理完
// 并且计数器降到零，才开始四次挥手
// shutdown(),直接关闭读写一端或两端，不关心计数器和缓冲区

// 只关闭写的一半
void sockets::shutdownWrite(int sockfd)
{
    if (::shutdown(sockfd, SHUT_WR) < 0)
        LOG_SYSERR << "sockets::shutdownWrite";
}

// 将IP以及端口信息转化成字符串
void sockets::toIpPort(char* buf, size_t size, const struct sockaddr* addr)
{
    if (addr->sa_family == AF_INET6)
    {
        buf[0] = '[';
        toIp(buf+1, size-1, addr);
        size_t end = ::strlen(buf);
        const struct sockaddr_in6* addr6 = sockaddr_in6_cast(addr);
        uint16_t port = sockets::networkToHost16(addr6->sin6_port);
        assert(size > end);
        snprintf(buf+end, size-end, "]:%u", port);
        return;
    }
    toIp(buf, size, addr);
    size_t end = ::strlen(buf);
    const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);
    uint16_t port = sockets::networkToHost16(addr4->sin_port);
    assert(size > end);
    snprintf(buf+end, size-end, ":%u", port);
}

// 将IP信息转化成字符串
void sockets::toIp(char* buf, size_t size, const struct sockaddr* addr)
{
    if (addr->sa_family == AF_INET)
    {
        assert(size >= INET_ADDRSTRLEN);
        const struct sockaddr_in* addr4 = sockaddr_in_cast(addr);
        ::inet_ntop(AF_INET, &addr4->sin_addr, buf, static_cast<socklen_t>(size));
    }
    else if (addr->sa_family == AF_INET6)
    {
        assert(size >= INET6_ADDRSTRLEN);
        const struct sockaddr_in6* addr6 = sockaddr_in6_cast(addr);
        ::inet_ntop(AF_INET6, &addr6->sin6_addr, buf, static_cast<socklen_t>(size));
    }
}

// 将字符串的IP和端口绑定到sockaddr_in IPv4结构体上
void sockets::fromIpPort(const char* ip, uint16_t port, struct sockaddr_in* addr)
{
    addr->sin_family = AF_INET;
    addr->sin_port = hostToNetwork16(port);
    if (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0)
        LOG_SYSERR << "sockets::fromIpPort";
}

// 将字符串的IP和端口绑定到sockaddr_in IPv6结构体上
void sockets::fromIpPort(const char* ip, uint16_t port, struct sockaddr_in6* addr)
{
    addr->sin6_family = AF_INET6;
    addr->sin6_port = hostToNetwork16(port);
    if (::inet_pton(AF_INET6, ip, &addr->sin6_addr) <= 0)
        LOG_SYSERR << "sockets::fromIpPort";
}

// 获得sockfd错误码
int sockets::getSocketError(int sockfd)
{
    int optval;
    socklen_t optlen = static_cast<socklen_t>(sizeof optval);

    if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
        return errno;
    else
        return optval;
}

// 获取套接字本地的sockaddr_in6 IPV6地址
struct sockaddr_in6 sockets::getLocalAddr(int sockfd)
{
    struct sockaddr_in6 localaddr;
    memZero(&localaddr, sizeof localaddr);
    socklen_t addrlen = static_cast<socklen_t>(sizeof localaddr);
    if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0)
        LOG_SYSERR << "sockets::getLocalAddr";
    return localaddr;
}

// 获取套接字对端的sockaddr_in6 IPV6地址
struct sockaddr_in6 sockets::getPeerAddr(int sockfd)
{
    struct sockaddr_in6 peeraddr;
    memZero(&peeraddr, sizeof peeraddr);
    socklen_t addrlen = static_cast<socklen_t>(sizeof peeraddr);
    if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen) < 0)
        LOG_SYSERR << "sockets::getPeerAddr";
    return peeraddr;
}


bool sockets::isSelfConnect(int sockfd)
{
    struct sockaddr_in6 localaddr = getLocalAddr(sockfd);
    struct sockaddr_in6 peeraddr = getPeerAddr(sockfd);
    if (localaddr.sin6_family == AF_INET)
    {
        const struct sockaddr_in* laddr4 = reinterpret_cast<struct sockaddr_in*>(&localaddr);
        const struct sockaddr_in* raddr4 = reinterpret_cast<struct sockaddr_in*>(&peeraddr);
        return laddr4->sin_port == raddr4->sin_port
            && laddr4->sin_addr.s_addr == raddr4->sin_addr.s_addr;
    }
    else if (localaddr.sin6_family == AF_INET6)
    {
        return localaddr.sin6_port == peeraddr.sin6_port
            && memcmp(&localaddr.sin6_addr, &peeraddr.sin6_addr, sizeof localaddr.sin6_addr) == 0;
    }
    else
        return false;
}

